package cn.texous.util.commons.util.encrypt.rsa;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * insert description here
 *
 * @author Showa.L
 * @since 2019/8/14 11:44
 */
public class RSABaseCrtUtils {

    /***/
    public static BigInteger p;
    /***/
    public static BigInteger q;
    /***/
    public static BigInteger n;
    /***/
    public static BigInteger e;
    /***/
    public static BigInteger d;

    static {
        // p = BigInteger.probablePrime(new Random().nextInt(100) + 100, new Random());
        // q = BigInteger.probablePrime(new Random().nextInt(100) + 100, new Random());
        // n = p.multiply(q);
        // BigInteger phi_n = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
        // do {
        //     e = new BigInteger(new Random().nextInt(phi_n.bitLength() - 1) + 1, new Random());
        // } while (e.compareTo(phi_n) != -1 || e.gcd(phi_n).intValue() != 1);
        // d = e.modPow(new BigInteger("-1"), phi_n);
        //
        p = new BigInteger("e86c7f16fd24818ffc502409d33a83c2a2a07fdfe9"
                + "71eb52de97a3de092980279ea29e32f378f5e6b7ab1049bb9e8c"
                + "5eae84dbf2847eb94ff14c1e84cf568415", 16);
        q = new BigInteger("d7d9d94071fcc67ede82084bbedeae1aaf"
                + "765917b6877f3193bbaeb5f9f36007127c9aa98d436a80b3cce3"
                + "fcd56d57c4103fb18f1819d5c238a49b0985fe7b49", 16);
        n = p.multiply(q);
        //        e = new BigInteger("65537");
        e = new BigInteger("10001", 16);
        BigInteger phi_n = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
        d = e.modPow(new BigInteger("-1"), phi_n);

        //        System.out.println(p.toString(16));
        //        System.out.println(q.toString(16));
        //        System.out.println(n.toString(16));
        //        System.out.println(e.toString(16));
        //        System.out.println(d.toString(16));
    }

    /**
     * 加密
     *
     * <p>C=M^e(mod n)</p>
     *
     * @param m m
     * @param n n
     * @param e e
     * @return
     */
    public static BigInteger encrypt(BigInteger m, BigInteger n, BigInteger e) {
        return m.modPow(e, n);
    }

    /**
     * 解密
     *
     * <p>M=C^d(mod n)</p>
     *
     * @param c c
     * @param n n
     * @param d d
     * @return
     */
    public static BigInteger decrypt(BigInteger c, BigInteger n, BigInteger d) {
        return c.modPow(d, n);
    }

    /**
     * 素数的概率性检验算法
     *
     * @param k k
     * @param n n
     * @return
     */
    public static boolean isPrime(int k, long n) {
        List<Long> a = new ArrayList<Long>();
        int t = n - 2 > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) (n - 2);
        do {
            long l = (long) (new Random().nextInt(t - 2) + 2);
            if (-1 == a.indexOf(l))
                a.add(l);
        } while (a.size() < k);
        for (int i = 0; i < k; i++)
            if (!miller(n, a.get(i)))
                return false;
        return true;
    }

    /**
     * @param n n
     * @param a a
     * @return
     */
    public static boolean miller(long n, long a) {
        long m = n - 1;
        int t = 0;
        while (m % 2 == 0) {
            m /= 2;
            t++;
        }
        long b = modExp(a, m, n);
        if (b == 1 || b == n - 1)
            return true;
        for (int j = 1; j < t; j++) {
            b = b * b % n;
            if (b == n - 1)
                return true;
        }
        return false;
    }

    /**
     * 模指运算
     *
     * @param b b
     * @param n n
     * @param m m
     * @return b^n(mod m)
     */
    public static long modExp(long b, long n, long m) {
        long result = 1;
        b = b % m;
        do {
            if ((n & 1) == 1)
                result = result * b % m;
            b = b * b % m;
            n = n >> 1;
        } while (n != 0);
        return result;
    }

    /**
     * 模逆运算
     *
     * @param b b
     * @param m m
     * @return b^-1(mod m)
     */
    public static long modInv(long b, long m) {
        if (b >= m) b %= m;
        return exGcd(b, m)[1] < 0 ? exGcd(b, m)[1] + m : exGcd(b, m)[1];
    }

    /**
     * 扩展欧几里德算法
     *
     * <p>(a,b)=ax+by
     *
     * @param a a
     * @param b b
     * @return 返回一个long数组result，result[0]=x，result[1]=y，result[2]=(a,b)
     */
    public static long[] exGcd(long a, long b) {
        if (a < b) {
            long temp = a;
            a = b;
            b = temp;
        }
        long[] result = new long[3];
        if (b == 0) {
            result[0] = 1;
            result[1] = 0;
            result[2] = a;
            return result;
        }
        long[] temp = exGcd(b, a % b);
        result[0] = temp[1];
        result[1] = temp[0] - a / b * temp[1];
        result[2] = temp[2];
        return result;
    }

}


